﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Xml.Linq;
using Phoenix.Xna.Framework.Properties;
using Phoenix.Xna.Framework.Reflection;
using Phoenix.Xna.Framework.Serialization.Markup.Objects;
using Phoenix.Xna.Framework.Xml;

namespace Phoenix.Xna.Framework.Serialization.Markup.Properties
{
    /// <summary>
    /// The intermediary markup property object for IDictionary properties.
    /// </summary>
    internal class DictionaryMarkupProperty : CollectionMarkupProperty
    {
        private List<IMarkupObject> _keys = new List<IMarkupObject>();      // The keys in the collection.
        private List<IMarkupObject> _items = new List<IMarkupObject>();     // The items in the collection.

        /// <summary>
        /// Initializes a new instance of the DictionaryMarkupProperty class.
        /// </summary>
        public DictionaryMarkupProperty()
            : base()
        {
        }

        #region IMarkupElement Members

        /// <summary>
        /// Serialize the element to XML.
        /// </summary>
        /// <returns>The element markup serialzed as an XElement.</returns>
        public override XElement Serialize()
        {
            XElement element = base.Serialize();

            for (int i = 0; i < _keys.Count; i++)
            {
                XElement keyValuePair = new XElement(AttributeIDs.KeyValuePairName);
                keyValuePair.SetAttribute(AttributeIDs.IsKeyValuePairAttribute, true);
                XElement key = _keys[i].Serialize();
                key.SetAttribute(AttributeIDs.IsDictionaryKeyAttribute, true);
                keyValuePair.Add(key);
                XElement item = _items[i].Serialize();
                item.SetAttribute(AttributeIDs.IsCollectionItemAttribute, true);
                keyValuePair.Add(item);
                element.Add(keyValuePair);
            }

            return element;
        }

        /// <summary>
        /// Deserialize the element from an XElement.
        /// </summary>
        /// <param name="element">The XElement to deserialize this element from.</param>
        public override void Deserialize(XElement element)
        {
            base.Deserialize(element);

            foreach (XElement node in element.Nodes())
            {
                if (node.ReadAttribute<bool>(AttributeIDs.IsKeyValuePairAttribute))
                {
                    foreach (XElement child in node.Nodes())
                    {
                        if (child.ReadAttribute<bool>(AttributeIDs.IsDictionaryKeyAttribute))
                        {
                            IMarkupObject keyMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(child);
                            _keys.Add(keyMarkup);
                            keyMarkup.Deserialize(child);
                        }
                        else if (child.ReadAttribute<bool>(AttributeIDs.IsCollectionItemAttribute))
                        {
                            IMarkupObject itemMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(child);
                            _items.Add(itemMarkup);
                            itemMarkup.Deserialize(child);
                        }
                    }
                }
            }
        }

        #endregion

        #region IMarkupProperty Members

        /// <summary>
        /// Extract the markup information from the PropertyInfo for this property.
        /// </summary>
        /// <param name="field">The PropertyInfo for this property.</param>
        /// <param name="value">The value of the property.</param>
        public override void ExtractMarkup(PropertyInfo property, object value)
        {
            base.ExtractMarkup(property, value);

            if (value != null && property.GetFirstAttributeOfType<SerializableIDictionaryAttribute>(false) != null)
            {
                if (!typeof(IDictionary).IsAssignableFrom(value.GetType()))
                {
                    throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, Resources.InvalidCastException, value.GetType().FullName, typeof(IList).Name));
                }

                IDictionary dictionary = value as IDictionary;
                ShouldPersist = dictionary.Count > 0;

                foreach (object key in dictionary.Keys)
                {
                    IMarkupObject keyMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(key);
                    _keys.Add(keyMarkup);
                    IMarkupObject itemMarkup = Serializer.MarkupSelector.GetMarkupObjectFor(dictionary[key]);
                    _items.Add(itemMarkup);
                }                
            }
        }

        /// <summary>
        /// Sets the value of MarkupProperty to the instance's property value.
        /// </summary>
        /// <param name="instance">The instance to set the property on.</param>
        public void SetValue(object instance)
        {
            base.SetValue(instance);
            IDictionary dictionary = (IDictionary)_value;

            for (int i = 0; i < _keys.Count; i++)
            {
                if (_keys[i].Instance == null)
                {
                    _keys[i].ConstructInstance();
                }

                if (_items[i].Instance == null)
                {
                    _items[i].ConstructInstance();
                }

                dictionary.Add(_keys[i].Instance, _items[i].Instance);
            }
        }

        #endregion
    }
}
